'''
1、网址：URL，uniform resource locator，指统一资源定位符，也即网络资源地址
     组成部分：
        ① 协议
        ② 域名
        ③ 资源路径
        ④ 查询参数（可选）
2、HTTP协议：hyperText transfer protocol-超文本传输协议，通过http协议规定浏览器和web服务器之间通讯的数据格式（浏览器和web服务器通信需使用http协议）
      超文本：除文本数据，还包括非文本数据（图片、音乐、视频等），超文本也即带链接的文本数据，常说的网页数据
      http协议设计目的是传输网页数据，如今可传输任意类型数据
      TCP传输协议为了保证网页中传输的数据安全性，http协议为了规定这些数据的具体格式
3、http请求/响应报文：
    组成：请求行（请求方式 请求资源路径 http协议版本）
         请求头
         空行
         请求体
     GET：获取web服务器数据
     POST：向web服务器提交数据（多一个请求体-可选）
4、http状态码：
     200 服务器成功处理请求
     400 请求地址或参数有误
     404 请求资源在服务器不存在
     500 服务器内部源代码有误
      （查看http：网页-右键-检查-console-输入：alert("nihao")-回车）
5、搭建python自带的静态web服务器
     终端输入：
        cd static/
        python3 -m http.server 端口号（不指定，默认8000）
6、开发自己的静态web服务器
     ① 编写一个tcp服务器程序
     ② 获取浏览器发送的http请求报文数据
     ③ 读取固定页面数据，把页面数据组装成http响应报文数据发送给浏览器
     ④ http响应报文数据发送完成，关闭服务于客户端的套接字
7、静态web服务器-面向对象开发
8、静态web服务器-命令行启动动态绑定端口号

# TODO 搭建python自带的静态服务器
import socket
import threading
if __name__ == '__main__':
    # ① 编写一个tcp服务器程序
    tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
    tcp_socket.bind(("", 8080))
    tcp_socket.listen(128)
    while True:
        # ② 获取浏览器发送的http请求报文数据
        conn, ip = tcp_socket.accept()         # 建立连接
        recv_data = conn.recv(1024).decode()   # 获取浏览器的请求信息
        print(recv_data)
        req_data = recv_data.split(" ")  # 获取用户请求资源的路径
        print(req_data)
        req_path = req_data[1]
        if req_path == "/":
            req_path = "/index.html"
        # ③ 读取固定页面数据，把页面数据组装成http响应报文数据发送给浏览器
        try:   # 判断请求的文件是否在服务端，组装404状态的响应报文，发送给浏览器
            with open("./static" + req_path, "rb") as f:  # (根据请求资源的路径，二进制方式读取指定文件的数据）
                f_data = f.read()
        except Exception as e:
            response_line = "HTTP/1.1 404 Not Found\r\n"  # 响应行
            response_header = "Server:pwb\r\n"  # 响应头
            response_body = "404 Not Found sorry"  # 响应体
            response_data = (response_line + response_header + "\r\n" + response_body).encode('utf-8')
            conn.send(response_data)
            # pass
        else:
            response_line = "HTTP/1.1 200 ok\r\n"  # 响应行
            response_header = "Server:pwb\r\n"     # 响应头
            response_body = f_data                 # 响应体
            response_data = (response_line + response_header + "\r\n").encode('utf-8') +response_body
            conn.send(response_data)
        # ④ 关闭服务于客户端的套接字
        finally:
            conn.close()

# TODO 静态web服务器--多任务版
import socket
import threading

def handle(conn):
    recv_data = conn.recv(1024).decode()
    req_data = recv_data.split(" ")
    print(req_data)
    if len(req_data) == 1:  # 判断客户端是否关闭
        conn.close()
        return
    req_path = req_data[1]
    if req_path == "/":
        req_path = "/index.html"
    try:
        with open("./static" + req_path, "rb") as f:
            f_data = f.read()
    except Exception as e:
        response_line = "/HTTP1.1 200 ok\r\n"
        response_header = "Server:pwb\r\n"
        response_body = f_data
        response_data = (response_line + response_header + "\r\n").encode('utf-8') + response_body
        conn.send(response_data)
    else:
        response_line = "/HTTP1.1 404 Not Fund\r\n"
        response_header = "Server:pwb\r\n"
        response_body = "404 Not Fund sorry"
        response_data = (response_line + response_header + "\r\n" + response_body).encode('utf-8')
        conn.send(response_data)
    finally:
        conn.close()
if __name__ == '__main__':
    tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
    tcp_socket.bind(("", 8080))
    tcp_socket.listen(128)
    while True:
        conn, ip = tcp_socket.accept()
        sub_thread = threading.Thread(target=handle, args=(conn, ))  # 当客户端和服务端连接成功，创建子线程，使用子线程专门处理客户端的需求，防止主线程阻塞
        sub_thread.start()


# TODO 静态web服务器--面向对象开发
import socket
import threading

#  把提供服务的web服务器抽象成一个类
class HttpWebServer:

    # 提供web服务器的初始化方法（int-创建socket对象、handle、start）
    def __int__(self):
        self.tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.tcp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
        self.tcp_socket.bind(("", 8080))
        self.tcp_socket.listen(128)

    def handle(self, conn):
        recv_data = conn.recv(1024).decode()
        req_data = recv_data.split(" ")
        print(req_data)
        if len(req_data) == 1:  # 判断客户端是否关闭
            conn.close()
            return
        req_path = req_data[1]
        if req_path == "/":
            req_path = "/index.html"
        try:
            with open("./static" + req_path, "rb") as f:
                f_data = f.read()
        except Exception as e:
            response_line = "/HTTP1.1 200 ok\r\n"
            response_header = "Server:pwb\r\n"
            response_body = f_data
            response_data = (response_line + response_header + "\r\n").encode('utf-8') + response_body
            conn.send(response_data)
        else:
            response_line = "/HTTP1.1 404 Not Fund\r\n"
            response_header = "Server:pwb\r\n"
            response_body = "404 Not Fund sorry"
            response_data = (response_line + response_header + "\r\n" + response_body).encode('utf-8')
            conn.send(response_data)
        finally:
            conn.close()

    # 开启web服务器方法，让web服务器处理客户端请求操作
    def start(self):
        while True:
            conn, ip = self.tcp_socket.accept()
            sub_thread = threading.Thread(target=self.handle, args=(conn, ))
            sub_thread.start()

if __name__ == '__main__':
    my_web = HttpWebServer()
    my_web.start()
'''

# TODO 静态web服务器-命令行启动动态绑定端口号
import socket
import threading
import sys
#  把提供服务的web服务器抽象成一个类
class HttpWebServer:

    # 提供web服务器的初始化方法（int-创建socket对象、handle、start）
    def __int__(self, port):
        self.tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.tcp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
        self.tcp_socket.bind(("", port))
        self.tcp_socket.listen(128)

    def handle(self, conn):
        recv_data = conn.recv(1024).decode()
        req_data = recv_data.split(" ")
        print(req_data)
        if len(req_data) == 1:  # 判断客户端是否关闭
            conn.close()
            return
        req_path = req_data[1]
        if req_path == "/":
            req_path = "/index.html"
        try:
            with open("./static" + req_path, "rb") as f:
                f_data = f.read()
        except Exception as e:
            response_line = "/HTTP1.1 200 ok\r\n"
            response_header = "Server:pwb\r\n"
            response_body = f_data
            response_data = (response_line + response_header + "\r\n").encode('utf-8') + response_body
            conn.send(response_data)
        else:
            response_line = "/HTTP1.1 404 Not Fund\r\n"
            response_header = "Server:pwb\r\n"
            response_body = "404 Not Fund sorry"
            response_data = (response_line + response_header + "\r\n" + response_body).encode('utf-8')
            conn.send(response_data)
        finally:
            conn.close()

    # 开启web服务器方法，让web服务器处理客户端请求操作
    def start(self):
        while True:
            conn, ip = self.tcp_socket.accept()
            sub_thread = threading.Thread(target=self.handle, args=(conn, ))
            sub_thread.start()

def main():
    print(sys.argv)  # 获取执行python程序的终端命令行参数
    if len(sys.argv) != 2:
        print("格式错误，应该为：python3 xxx.py 9090")
        return
    if not sys.argv[1].isdigit():  # 判读参数是否为整型
        print("格式错误，应该为：python3 xxx.py 9090")
        return
    port = int(sys.argv[1])  # 获取动态端口号（为字符串，需转换为int）
    my_web = HttpWebServer(port)
    my_web.start()
if __name__ == '__main__':
    main()







